#    This file is part of the Minecraft Overviewer.
#
#    Minecraft Overviewer is free software: you can redistribute it and/or
#    modify it under the terms of the GNU General Public License as published
#    by the Free Software Foundation, either version 3 of the License, or (at
#    your option) any later version.
#
#    Minecraft Overviewer is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
#    Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with the Overviewer.  If not, see <http://www.gnu.org/licenses/>.
from datetime import datetime, timedelta

from PIL import Image
from . import textures

"""The contents of this file are imported into the namespace of config files.
It also defines the render primitive objects, which are used by the C code.
Each render primitive has a corresponding section of C code, so both places
must be changed simultaneously if you want to make any changes.

"""

class RenderPrimitive(object):
    options = {}
    name = None
    def __init__(self, **kwargs):
        if self.name is None:
            raise RuntimeError("RenderPrimitive cannot be used directly")
        
        self.option_values = {}
        for key, val in kwargs.items():
            if not key in self.options:
                raise ValueError("primitive `{0}' has no option `{1}'".format(self.name, key))
            self.option_values[key] = val
        
        # set up defaults
        for name, (description, default) in self.options.items():
            if not name in self.option_values:
                self.option_values[name] = default

class Base(RenderPrimitive):
    name = "base"
    options = {
        "biomes": ("whether or not to use biomes", True),
    }

class NetherOld(RenderPrimitive):
    name = "netherold"

class Nether(RenderPrimitive):
    name = "nether"

class HeightFading(RenderPrimitive):
    name = "height-fading"
    options = {
        # 128 is *WRONG*, it should be 64. but we're grandfathered in for now
        "sealevel": ("target sea level", 128),
    }
    
    black_color = Image.new("RGB", (24,24), (0,0,0))
    white_color = Image.new("RGB", (24,24), (255,255,255))

class Depth(RenderPrimitive):
    name = "depth"
    options = {
        "min": ("lowest level of blocks to render", 0),
        "max": ("highest level of blocks to render", 255),
    }
    
class Exposed(RenderPrimitive):
    name = "exposed"
    options = {
        "mode": ("0 = exposed blocks only, 1 = unexposed blocks only", 0),
    }
    
class NoFluids(RenderPrimitive):
    name = "no-fluids"

class EdgeLines(RenderPrimitive):
    name = "edge-lines"
    options = {
        "opacity": ("darkness of the edge lines, from 0.0 to 1.0", 0.15),
    }

class Cave(RenderPrimitive):
    name = "cave"
    options = {
        "only_lit": ("only render lit caves", False),
    }

class DepthTinting(RenderPrimitive):
    name = "depth-tinting"
    
    @property
    def depth_colors(self):
        depth_colors = getattr(self, "_depth_colors", [])
        if depth_colors:
            return depth_colors
        r = 255
        g = 0
        b = 0
        for z in range(128):
            depth_colors.append(r)
            depth_colors.append(g)
            depth_colors.append(b)
            
            if z < 32:
                g += 7
            elif z < 64:
                r -= 7
            elif z < 96:
                b += 7
            else:
                g -= 7

        self._depth_colors = depth_colors
        return depth_colors

class Lighting(RenderPrimitive):
    name = "lighting"
    options = {
        "strength": ("how dark to make the shadows, from 0.0 to 1.0", 1.0),
        "night": ("whether to use nighttime skylight settings", False),
        "color": ("whether to use colored light", False),
    }

    @property
    def facemasks(self):
        facemasks = getattr(self, "_facemasks", None)
        if facemasks:
            return facemasks
        
        white = Image.new("L", (24,24), 255)
        
        top = Image.new("L", (24,24), 0)
        left = Image.new("L", (24,24), 0)
        whole = Image.new("L", (24,24), 0)
        
        toppart = textures.Textures.transform_image_top(white)
        leftpart = textures.Textures.transform_image_side(white)
        
        # using the real PIL paste here (not alpha_over) because there is
        # no alpha channel (and it's mode "L")
        top.paste(toppart, (0,0))
        left.paste(leftpart, (0,6))
        right = left.transpose(Image.FLIP_LEFT_RIGHT)
        
        # Manually touch up 6 pixels that leave a gap, like in
        # textures._build_block()
        for x,y in [(13,23), (17,21), (21,19)]:
            right.putpixel((x,y), 255)
        for x,y in [(3,4), (7,2), (11,0)]:
            top.putpixel((x,y), 255)
    
        # special fix for chunk boundary stipple
        for x,y in [(13,11), (17,9), (21,7)]:
            right.putpixel((x,y), 0)
        
        self._facemasks = (top, left, right)
        return self._facemasks

class SmoothLighting(Lighting):
    name = "smooth-lighting"

class ClearBase(RenderPrimitive):
    name = "clear-base"

class Overlay(RenderPrimitive):
    name = "overlay"

    options = {
        'overlay_color' : ('a tuple of (r, g, b, a) for coloring the overlay', None),
    }

    @property
    def whitecolor(self):
        whitecolor = getattr(self, "_whitecolor", None)
        if whitecolor:
            return whitecolor
        white = Image.new("RGBA", (24,24), (255, 255, 255, 255))
        self._whitecolor = white
        return white
    
    @property
    def facemask_top(self):
        facemask_top = getattr(self, "_facemask_top", None)
        if facemask_top:
            return facemask_top
        
        white = Image.new("L", (24,24), 255)
        top = Image.new("L", (24,24), 0)
        toppart = textures.Textures.transform_image_top(white)
        top.paste(toppart, (0,0))
        for x,y in [(3,4), (7,2), (11,0)]:
            top.putpixel((x,y), 255)
        self._facemask_top = top
        return top

class SpawnOverlay(Overlay):
    name = "overlay-spawn"

class SlimeOverlay(Overlay):
    name = "overlay-slime"


class StructureOverlay(Overlay):
    name = "overlay-structure"
    options = {
        'structures': ('a list of ((((relx, rely, relz), blockid), ...), (r, g, b, a)) tuples for coloring minerals',
                       [(((0, 0, 0, 66),  (0, -1, 0, 4)), (255, 0,   0, 255)),
                        (((0, 0, 0, 27),  (0, -1, 0, 4)), (0,   255, 0, 255)),
                        (((0, 0, 0, 28),  (0, -1, 0, 4)), (255, 255, 0, 255)),
                        (((0, 0, 0, 157), (0, -1, 0, 4)), (255, 100, 0, 255)),
                       ]),
    }


class MineralOverlay(Overlay):
    name = "overlay-mineral"
    options = {
        'minerals' : ('a list of (blockid, (r, g, b)) tuples for coloring minerals', None),
    }

class BiomeOverlay(Overlay):
    name = "overlay-biomes"
    options = {
        'biomes' : ('a list of (biome, (r, g, b)) tuples for coloring biomes', None),
        'alpha'  : ('an integer value between 0 (transparent) and 255 (opaque)', None),
    }

class HeatmapOverlay(Overlay):
    t_now = datetime.now()
    name = "overlay-heatmap"
    options = {
        't_invisible': (
            'the timestamp when the overlay will get invisible (e.g. 1 month go)',
            int((t_now - timedelta(days=30)).timestamp())
        ),
        't_full': ('the timestamp when the overlay will be fully visible (e.g. now)', int(t_now.timestamp())),
    }

class Hide(RenderPrimitive):
    name = "hide"
    options = {
        'blocks' : ('a list of blockids or (blockid, data) tuples of blocks to hide', []),
    }

# Built-in rendermodes for your convenience!
normal = [Base(), EdgeLines()]
lighting = [Base(), EdgeLines(), Lighting()]
smooth_lighting = [Base(), EdgeLines(), SmoothLighting()]
night = [Base(), EdgeLines(), Lighting(night=True)]
smooth_night = [Base(), EdgeLines(), SmoothLighting(night=True)]
netherold = [Base(), EdgeLines(), NetherOld()]
netherold_lighting = [Base(), EdgeLines(), NetherOld(), Lighting()]
netherold_smooth_lighting = [Base(), EdgeLines(), NetherOld(), SmoothLighting()]
nether = [Base(), EdgeLines(), Nether()]
nether_lighting = [Base(), EdgeLines(), Nether(), Lighting()]
nether_smooth_lighting = [Base(), EdgeLines(), Nether(), SmoothLighting()]
cave = [Base(), EdgeLines(), Cave(), DepthTinting()]
